`timescale 1ns / 1ps
/*
 Copyright 2020 Sean Xiao, jxzsxsp@qq.com
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

module uart_transceiver(
    input sys_clk,

    input uart_rx,
    output reg uart_tx,

    input [15:0] divisor,

    output reg [7:0] rx_data,
    output reg rx_done,
    output rx_eop,

    input [7:0] tx_data,
    input tx_wr,
    output reg tx_done,
    output reg tx_busy,
    
    output test_pin,
    
    input sys_rst
);

//-----------------------------------------------------------------
// enable8 generator
//-----------------------------------------------------------------
reg [15:0] enable8_counter;

wire enable8;
assign enable8 = (enable8_counter == 0) ? 1'b1 : 1'b0;

reg bt8_tongle = 0;
always @(posedge sys_clk)
if(sys_rst)
    enable8_counter <= divisor - 1;
else 
begin
    if(enable8)
    begin
        bt8_tongle <= ~bt8_tongle;
        
        if(bt8_tongle) enable8_counter <= divisor - 1;
        else enable8_counter <= divisor - 2;
    end
    else  enable8_counter <= enable8_counter - 1;
end

assign test_pin = enable8;
//-----------------------------------------------------------------
// Synchronize uart_rx
//-----------------------------------------------------------------
reg uart_rx1;
reg uart_rx2;

always @(posedge sys_clk) 
begin
    uart_rx1 <= uart_rx;
    uart_rx2 <= uart_rx1;
end

//-----------------------------------------------------------------
// UART RX Logic
//-----------------------------------------------------------------
reg rx_busy;
reg [2:0] rx_count8;
reg [3:0] rx_bitcount;
reg [7:0] rx_reg;

always @(posedge sys_clk)
if(sys_rst) 
begin
    rx_done <= 1'b0;
    rx_busy <= 1'b0;
    rx_count8  <= 4'd0;
    rx_bitcount <= 4'd0;
end 
else 
begin
    rx_done <= 1'b0;

    if(enable8) 
    begin
        if(~rx_busy) 
        begin // look for start bit
            if(~uart_rx2) 
            begin // start bit found
                rx_busy <= 1'b1;
                rx_count8 <= 4'd3;
                rx_bitcount <= 4'd0;
            end
        end 
        else 
        begin
            rx_count8 <= rx_count8 + 4'd1;

            if(rx_count8 == 4'd0) 
            begin // sample
                rx_bitcount <= rx_bitcount + 4'd1;

                if(rx_bitcount == 4'd0) 
                begin // verify startbit
                    if(uart_rx2)
                        rx_busy <= 1'b0;
                end 
                else if(rx_bitcount == 4'd9) 
                begin
                    rx_busy <= 1'b0;
                    if(uart_rx2) 
                    begin // stop bit ok
                        rx_data <= rx_reg;
                        rx_done <= 1'b1;
                    end // ignore RX error
                end 
                else
                    rx_reg <= {uart_rx2, rx_reg[7:1]};
            end
        end
    end
end


reg [2:0] baudtick_cnt = 1;
always @(posedge sys_clk)
if(sys_rst) baudtick_cnt <= 1;
else if(enable8) baudtick_cnt <= baudtick_cnt + 1;

wire rx_baudtick = (baudtick_cnt == 0) ? enable8 : 0;

localparam RX_EOP_BIT = 5;
reg [RX_EOP_BIT - 1:0] rx_done_r = 0;

reg [1:0] eop_st = 0;
reg eop_r = 0;
always @(posedge sys_clk)
if(sys_rst) 
begin
    eop_r <= 0;
    rx_done_r <= 0;
    eop_st <= 0;
end
else case(eop_st)
0:
begin
    eop_r <= 0;
    rx_done_r <= 0;

    if(rx_done) 
    begin
        eop_st <= 1;
    end
end
1:
begin
    if(uart_rx2)
    begin
        if(rx_baudtick) rx_done_r <= {rx_done_r[RX_EOP_BIT - 2: 0], 1'b1}; 
        if(&rx_done_r)
        begin
            eop_r <= 1;
            eop_st <= 0;
        end
    end
    else eop_st <= 0;
end
endcase


assign rx_eop = eop_r;
//-----------------------------------------------------------------
// UART TX Logic
//-----------------------------------------------------------------
reg [3:0] tx_bitcount;
reg [2:0] tx_count8;
reg [7:0] tx_reg;

always @(posedge sys_clk)
if(sys_rst) 
begin
    tx_done <= 1'b0;
    tx_busy <= 1'b0;
    uart_tx <= 1'b1;
end 
else 
begin
    if(tx_wr) 
    begin
        tx_done <= 1'b0;
        tx_reg <= tx_data;
        tx_bitcount <= 4'd0;
        tx_count8 <= 4'd1;
        tx_busy <= 1'b1;
        uart_tx <= 1'b0;
`ifdef SIMULATION
        $display("UART: %c", tx_data);
`endif
    end 
    else if(enable8 && tx_busy) 
    begin
        tx_count8  <= tx_count8 + 4'd1;

        if(tx_count8 == 4'd0) 
        begin
            tx_bitcount <= tx_bitcount + 4'd1;
            
            if(tx_bitcount == 4'd8) uart_tx <= 1'b1;
            else if(tx_bitcount == 4'd9) 
            begin
                uart_tx <= 1'b1;
                tx_busy <= 1'b0;
                tx_done <= 1'b1;
            end 
            else 
            begin
                uart_tx <= tx_reg[0];
                tx_reg <= {1'b0, tx_reg[7:1]};
            end
        end
    end
end


endmodule
